/**
 * MyTableGrid, version 1.0.8
 *
 * Dual licensed under the MIT and GPL licenses.
 *
 * Copyright 2009 Pablo Aravena, all rights reserved.
 * http://pabloaravena.info/mytablegrid
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
var MyTableGrid = Class.create();

MyTableGrid.ADD_BTN = 1;
MyTableGrid.DEL_BTN = 4;
MyTableGrid.SAVE_BTN = 8;

MyTableGrid.prototype = {
    version: '1.0.8',

    _imageRefs : {
        colMoveTop: '../images/mtg/col-move-top.gif',
        colMoveBottom: '../images/mtg/col-move-bottom.gif',
        sortAscendingIcon: '../images/mtg/sort_ascending.png',
        sortDescendingIcon: '../images/mtg/sort_descending.png',
        prevIcon: '../images/mtg/prev.gif',
        nextIcon: '../images/mtg/next.gif',
        firstIcon: '../images/mtg/first.gif',
        lastIcon: '../images/mtg/last.gif',
        prevDisabledIcon: '../images/mtg/prev-disabled.gif',
        nextDisabledIcon: '../images/mtg/next-disabled.gif',
        firstDisabledIcon: '../images/mtg/first-disabled.gif',
        lastDisabledIcon: '../images/mtg/last-disabled.gif',
        ySepIcon: '../images/mtg/y-sep.png',
        mtgLoaderIcon: '../images/mtg/mtg-loader.gif',
        mtgSortArrowAscIcon: '../images/mtg/sort-arrow-asc.png',
        mtgSortArrowDescIcon: '../images/mtg/sort-arrow-desc.png',
        mtgMagnifierIcon: '../images/mtg/magnifier.png'
    },

    _messages : {
        totalDisplayMsg: '<strong>{total}</strong> records found',
        rowsDisplayMsg: ', displaying <strong>{from}</strong> to <strong>{to}</strong>',
        pagePromptMsg: '<td><strong>Page:</strong></td><td>{input}</td><td>of <strong>{pages}</strong></td>',
        pagerNoDataFound: '<strong>No records found</strong>',
        add: 'Add',
        remove: 'Delete',
        save: 'Save',
        sortAsc: 'Sort Ascending',
        sortDesc: 'Sort Descending',
        selectAll: 'Select All'
    },

    /**
     * MyTableGrid constructor
     */
    initialize : function(tableModel) {
        this._mtgId = $$('.myTableGrid').length + 1;
        this.tableModel = tableModel;
        this.columnModel = tableModel.columnModel || [];
        this.rows = tableModel.rows || [];
        this.options = tableModel.options || {};
        this.name = tableModel.name || '';
        this.fontSize = 11;
        this.headerHeight = 24;
        this.cellHeight = 24;
        this.pagerHeight = 24;
        this.scrollBarWidth = 18;
        this.topPos = 0;
        this.leftPos = 0;
        this.selectedHCIndex = 0;
        this.pager = this.options.pager || null;
        if (this.options.pager) this.pager.pageParameter = this.options.pager.pageParameter || 'page';
        this.url = tableModel.url || null;
        this.request = tableModel.request || {};
        this.sortColumnParameter = this.options.sortColumnParameter || 'sortColumn';
        this.ascDescFlagParameter = this.options.ascDescFlagParameter || 'ascDescFlg';
        this.sortedColumnIndex = 0;
        this.onCellFocus = this.options.onCellFocus || null;
        this.onCellBlur = this.options.onCellBlur || null;
        this.modifiedRows = []; //will contain the modified row numbers
        this.afterRender = this.options.afterRender || null; //after rendering handler
        this.rowStyle = this.options.rowStyle || null; //row style handler
        this.rowClass = this.options.rowClass || null; //row class handler
        this.addSettingBehaviorFlg = (this.options.addSettingBehavior == undefined || this.options.addSettingBehavior)? true : false; 

        this.renderedRows = 0; //Use for lazy rendering
        this.renderedRowsAllowed = 0; //Use for lazy rendering depends on bodyDiv height
        this.newRowsAdded = [];
        this.deletedRows = [];
        for (var i = 0; i < this.columnModel.length; i++) {
            if (!this.columnModel[i].hasOwnProperty('editable')) this.columnModel[i].editable = false;
            if (!this.columnModel[i].hasOwnProperty('visible')) this.columnModel[i].visible = true;
            if (!this.columnModel[i].hasOwnProperty('sortable')) this.columnModel[i].sortable= true;
            if (!this.columnModel[i].hasOwnProperty('type')) this.columnModel[i].type = 'string';
            this.columnModel[i].positionIndex = i;
        }

        this.targetColumnId = null;
        this.editedCellId = null;

        this.gap = 2; //diff between width and offsetWidth
        if (Prototype.Browser.WebKit) this.gap = 0;
    },

    /**
     * Renders the table grid control into a given target
     */
    render : function(target) {
        this.target = target;
        $(target).innerHTML = this._createTableLayout();
        var id = this._mtgId;
        this.tableDiv = $('myTableGrid' + id);
        this.headerTitle = $('mtgHeaderTitle'+id);
        this.headerToolbar = $('mtgHeaderToolbar'+id);
        this.headerRowDiv = $('headerRowDiv' + id);
        this.bodyDiv = $('bodyDiv' + id);
        this.innerBodyDiv = $('innerBodyDiv' + id);
        this.pagerDiv = $('pagerDiv' + id);
        this.resizeMarkerLeft = $('resizeMarkerLeft' + id);
        this.resizeMarkerRight = $('resizeMarkerRight' + id);
        this.dragColumn = $('dragColumn' + id);
        this.colMoveTopDiv = $('mtgColMoveTop' + id);
        this.colMoveBottomDiv = $('mtgColMoveBottom' + id);
        this.scrollLeft = 0;
        this.scrollTop = 0;
        this.targetColumnId = null;

        var self = this;

        Event.observe(this.bodyDiv, 'dom:dataLoaded', function(){
            self._showLoaderSpinner();
            if (self.rows.length > 0) {
                self.bodyTable = $('mtgBT' + id);
                self._applyCellCallbacks();
                self._applyHeaderButtons();
                self._makeAllColumnResizable();
                self._makeAllColumnDraggable();
                if (self.addSettingBehaviorFlg) self._applySettingMenuBehavior();
                self.keys = new KeyTable(self);
                self._addKeyBehavior();
            }
            if (self.pager) {
                self._addPagerBehavior();
            }
            if (self.afterRender) {
                self.afterRender();
            }
            self._hideLoaderSpinner();
        });

        setTimeout(function() {
            self.renderedRowsAllowed = Math.floor(self.bodyDiv.clientHeight / self.cellHeight);
            if (self.tableModel.hasOwnProperty('rows')) {
                if (self.rows.length > 0) {
                    self.innerBodyDiv.innerHTML = self._createTableBody(self.rows);
                    if (self.pager) {
                        self.pager.total = self.rows.length;
                        self.pagerDiv.innerHTML = self._updatePagerInfo();
                    }
                    self.bodyDiv.fire('dom:dataLoaded');
                } else {
                    self.pagerDiv.innerHTML = self._updatePagerInfo();
                }
            } else {
                self._retrieveDataFromUrl(1, true);
            }
        }, 0);

        if (this.options.toolbar) {
            var elements = this.options.toolbar.elements || [];
            if (elements.indexOf(MyTableGrid.ADD_BTN) >= 0) {
                Event.observe($('mtgAddBtn'+id), 'click', function() {
                    self.addNewRow();
                    if (self.options.toolbar.onAdd) {
                        self.options.toolbar.onAdd.call();
                    }
                });
            }

            if (elements.indexOf(MyTableGrid.DEL_BTN) >= 0) {
                Event.observe($('mtgDelBtn'+id), 'click', function() {
                    self.deleteRows();
                    if (self.options.toolbar.onDelete) {
                        self.options.toolbar.onDelete.call();
                    }
                });
            }

            if (elements.indexOf(MyTableGrid.SAVE_BTN) >= 0) {
                Event.observe($('mtgSaveBtn'+id), 'click', function() {
                    if (self.options.toolbar.onSave) {
                        self.options.toolbar.onSave.call();
                    }
                });
            }
        }
        // Adding scrolling handler
        Event.observe($('bodyDiv' + id), 'scroll', function() {
            self._syncScroll();
        });
        // Adding resize handler
        Event.observe(window, 'resize', function() {
            self.resize();
        });

    },

    /**
     * Creates the table layout
     */
    _createTableLayout : function() {
        var target = $(this.target);
        var width = this.options.width || (target.getWidth() - this._fullPadding(target,'left') - this._fullPadding(target,'right')) + 'px';
        var height = this.options.height || (target.getHeight() - this._fullPadding(target,'top') - this._fullPadding(target,'bottom')) + 'px';
        var id = this._mtgId;
        var cm = this.columnModel;
        var gap = this.gap;
        var ir = this._imageRefs;

        this.tableWidth = parseInt(width) - 2;
        this.tableHeight = parseInt(height) - 2;

        //Calculates header width
        var hw = 0;
        for (var i = 0; i < cm.length; i++) {
            hw += parseInt(cm[i].width) + gap;
        }
        this.headerWidth = hw;
        var idx = 0;
        var html = [];
        html[idx++] = '<div id="myTableGrid'+id+'" class="myTableGrid" style="position:relative;width:'+this.tableWidth+'"px;height:'+this.tableHeight+'px;z-index:0">';

        if (this.options.title) { // Adding header title
            html[idx++] = '<div id="mtgHeaderTitle'+id+'" class=" mtgHeaderTitle" style="position:absolute;top:'+this.topPos+'px;left:'+this.leftPos+'px;width:'+(this.tableWidth - 6)+'px;height:'+(this.headerHeight - 6)+'px;padding:3px;z-index:10">';
            html[idx++] = this.options.title;
            html[idx++] = '</div>';
            this.topPos += this.headerHeight + 1;
        }

        if (this.options.toolbar) {
            var elements = this.options.toolbar.elements || [];
            html[idx++] = '<div id="mtgHeaderToolbar'+id+'" class="mtgToolbar" style="position:absolute;top:'+this.topPos+'px;left:'+this.leftPos+'px;width:'+(this.tableWidth - 4)+'px;height:'+(this.headerHeight - 4)+'px;padding:2px;z-index:10">';
            var beforeFlg = false;
            if(elements.indexOf(MyTableGrid.SAVE_BTN) >= 0) {
                html[idx++] = '<a href="#" class="toolbarbtn"><span class="savebutton" id="mtgSaveBtn'+id+'">'+this._messages.save+'</span></a>';
                beforeFlg = true;
            }
            if(elements.indexOf(MyTableGrid.ADD_BTN) >= 0) {
                if (beforeFlg) html[idx++] = '<div class="toolbarsep">&#160;</div>';
                html[idx++] = '<a href="#" class="toolbarbtn"><span class="addbutton" id="mtgAddBtn'+id+'">'+this._messages.add+'</span></a>';
                beforeFlg = true;
            }
            if(elements.indexOf(MyTableGrid.DEL_BTN) >= 0) {
                if (beforeFlg) html[idx++] = '<div class="toolbarsep">&#160;</div>';
                html[idx++] = '<a href="#" class="toolbarbtn"><span class="delbutton" id="mtgDelBtn'+id+'">'+this._messages.remove+'</span></a>';
            }
            html[idx++] = '</div>';
            this.topPos += this.headerHeight + 1;
        }
        
        // Adding Header Row
        html[idx++] = '<div id="headerRowDiv'+id+'" class="mtgHeaderRow" style="position:absolute;top:'+this.topPos+'px;left:'+this.leftPos+'px;width:'+this.tableWidth+'px;height:'+this.headerHeight+'px;padding:0;overflow:hidden;z-index:0">';
        //header row box useful for drag and drop
        html[idx++] = '<div id="mtgHRB'+id+'" style="position:relative;padding:0;margin:0;width:'+this.headerWidth+'px">';
        // Adding Header Row Cells
        html[idx++] = this._createHeaderRow();
        html[idx++] = '</div>'; // closes mtgHRB
        html[idx++] = '</div>'; // closes headerRowDiv
        this.topPos += this.headerHeight + 1;

        // Adding Body Area
        this.bodyHeight = this.tableHeight - this.headerHeight - 3;
        if (this.options.title) this.bodyHeight = this.bodyHeight - this.headerHeight - 1;
        if (this.options.pager) this.bodyHeight = this.bodyHeight - this.pagerHeight - 1;
        if (this.options.toolbar) this.bodyHeight = this.bodyHeight - this.pagerHeight - 1;
        html[idx++] = '<div id="bodyDiv'+id+'" class="mtgBody" style="position:absolute;top:'+this.topPos+'px;left:'+this.leftPos+'px;width:'+this.tableWidth+'px;height:'+this.bodyHeight+'px;overflow:auto;z-index:10">';
        html[idx++] = '<div id="innerBodyDiv'+id+'" class="mtgInnerBody" style="position:absolute;top:0px;left:'+this.leftPos+'px;width:'+(this.tableWidth - this.scrollBarWidth)+'px;overflow:none;z-index:0">';
        html[idx++] = '</div>'; // closes innerBodyDiv
        html[idx++] = '</div>'; // closes bodyDiv

        // Adding Pager Panel
        if (this.pager) {
            this.topPos += this.bodyHeight + 2;
            html[idx++] = '<div id="pagerDiv'+id+'" class="mtgPager" style="position:absolute;top:'+this.topPos+'px;left:0;bottom:0;width:'+(this.tableWidth - 4)+'px;height:'+(this.pagerHeight - 4)+'px">';
            html[idx++] = this._updatePagerInfo(true);
            html[idx++] = '</div>'; // closes Pager Div
        }

        // Adding Table Setting Button Control
        if (this.addSettingBehaviorFlg) {
            html[idx++] = '<div id="mtgSB'+id+'" class="mtgSettingButton">';
            html[idx++] = '</div>';
            // Adding Table Setting Menu
            html[idx++] = this._createSettingMenu();
        }

        // Adding Header Button Control
        html[idx++] = '<div id="mtgHB'+id+'" class="mtgHeaderButton" style="width:14px;height:'+this.headerHeight+'px">';
        html[idx++] = '</div>';
        // Adding Header Button Menu
        html[idx++] = '<div id="mtgHBM'+id+'" class="mtgMenu">';
        html[idx++] = '<ul>';
        html[idx++] = '<li>';
        html[idx++] = '<a id="mtgSortAsc'+id+'" class="mtgMenuItem" href="javascript:void(0)">';
        html[idx++] = '<table cellspacing="0" cellpadding="0" width="100%" border="0">';
        html[idx++] = '<tr><td width="25"><img src="'+ir.sortAscendingIcon+'" width="16" height="16" border="0" class="mtgMenuItemIcon mtgSortAscendingIcon"></td>';
        html[idx++] = '<td>'+this._messages.sortAsc+'</td></tr></table>';
        html[idx++] = '</a>';
        html[idx++] = '</li>';
        html[idx++] = '<li>';
        html[idx++] = '<a id="mtgSortDesc'+id+'" class="mtgMenuItem" href="javascript:void(0)">';
        html[idx++] = '<table cellspacing="0" cellpadding="0" width="100%" border="0">';
        html[idx++] = '<tr><td width="25"><img src="'+ir.sortDescendingIcon+'" width="16" height="16" border="0" class="mtgMenuItemIcon mtgSortDescendingIcon"></td>';
        html[idx++] = '<td>'+this._messages.sortDesc+'</td></tr></table>';
        html[idx++] = '</a>';
        html[idx++] = '</li>';
        html[idx++] = '<li class="mtgSelectAll">';
        html[idx++] = '<a class="mtgMenuItem" href="javascript:void(0)">';
        html[idx++] = '<table cellspacing="0" cellpadding="0" width="100%" border="0">';
        html[idx++] = '<tr><td width="25"><span class="mtgMenuItemChk"><input type="checkbox" id="mtgSelectAll'+id+'"></span></td>';
        html[idx++] = '<td>'+this._messages.selectAll+'</td></tr></table>';
        html[idx++] = '</a>';
        html[idx++] = '</li>';
        html[idx++] = '</ul>';
        html[idx++] = '</div>';

        // Adding resize markers
        html[idx++] = '<div id="resizeMarkerLeft'+id+'" class="mtgResizeMarker">';
        html[idx++] = '</div>';
        html[idx++] = '<div id="resizeMarkerRight'+id+'" class="mtgResizeMarker">';
        html[idx++] = '</div>';

        html[idx++] = '</div>'; // closes Table Div;

        // Adding Dragging controls
        html[idx++] = '<div id="mtgColMoveTop'+id+'" style="position:absolute;width:9px;height:9px;top:0;left:0;z-index:50;visibility:hidden">';
        html[idx++] = '<img src="'+ir.colMoveTop+'" width="9" height="9">';
        html[idx++] = '</div>';

        html[idx++] = '<div id="mtgColMoveBottom'+id+'" style="position:absolute;width:9px;height:9px;top:0;left:0;z-index:50;visibility:hidden">';
        html[idx++] = '<img src="'+ir.colMoveBottom+'" width="9" height="9">';
        html[idx++] = '</div>';

        html[idx++] = '<div id="dragColumn'+id+'" class="dragColumn" style="width:100px;height:18px;">';
        html[idx++] = '<span class="columnTitle">&nbsp;</span>';
        html[idx++] = '<div class="drop-no">&nbsp;</div>';
        html[idx++] = '</div>';
        return html.join('');
    },

    /**
     * Creates the Header Row
     */
    _createHeaderRow : function() {
        // Templates definition
        var thTmpl = '<th id="mtgHC{id}_{x}" width="{width}" style="width:{width}px;padding:0;margin:0" class="mtgHeaderCell mtgHC{id}">';
        var thTmplLast = '<th id="mtgHC{id}_{x}" width="{width}" style="width:{width}px;padding:0;margin:0;border-right:none" class="mtgHeaderCell mtgHC{id}">';
        var ihcTmpl = '<div id="mtgIHC{id}_{x}" class="mtgInnerHeaderCell mtgIHC{id}" style="position:relative;width:{width}px;height:{height}px;padding:3px;z-index:20">';
        var ihcTmplLast = '<div id="mtgIHC{id}_{x}" class="mtgInnerHeaderCell" style="position:relative;width:{width}px;height:{height}px;padding:3px;z-index:20">';
        var hsTmpl = '<div id="mtgHS{id}_{x}" class="mtgHS mtgHS{id}" style="position:absolute;top:0;right:-4px;width:1px;height:{height}px">';
        var siTmpl = '<img id="mtgSortIcon{id}_{x}" width="8" height="4" style="display:none">';

        var cm = this.columnModel;
        var id = this._mtgId;
        var gap = (this.gap == 0)? 2 : 0;
        var cellHeight = this.headerHeight - 6;

        var idx = 0;
        var html = [];
        html[idx++] = '<table id="mtgHRT'+id+'" width="'+this.headerWidth+'" cellpadding="0" cellspacing="0" border="0" class="mtgHeaderRowTable">';
        html[idx++] = '<thead>';
        html[idx++] = '<tr>';
        // Creating Header Cell Elements
        var temp = null;
        for (var i = 0; i < cm.length; i++) {
            var cell = cm[i];
            temp = thTmpl.replace(/\{id\}/g, id);
            temp = temp.replace(/\{x\}/g, i);
            temp = temp.replace(/\{width\}/g, parseInt(cell.width));
            html[idx++] = temp;
            temp = ihcTmpl.replace(/\{id\}/g, id);
            temp = temp.replace(/\{x\}/g, i);
            temp = temp.replace(/\{width\}/g, (parseInt(cell.width) - 8 - gap));
            temp = temp.replace(/\{height\}/g, cellHeight);
            html[idx++] = temp;
            html[idx++] = cell.title;
            temp = siTmpl.replace(/\{id\}/g, id);
            temp = temp.replace(/\{x\}/g, i);
            html[idx++] = temp;
            temp = hsTmpl.replace(/\{id\}/g, id);
            temp = temp.replace(/\{x\}/g, i);
            temp = temp.replace(/\{height\}/g, this.headerHeight);
            html[idx++] = temp;
            html[idx++] = '</div>';
            html[idx++] = '</div>';
            html[idx++] = '</th>';
        }
        // Last Header Element
        temp = thTmplLast.replace(/\{id\}/g, id);
        temp = temp.replace(/\{x\}/g, i);
        temp = temp.replace(/\{width\}/g, '20');
        html[idx++] = temp;
        temp = ihcTmplLast.replace(/\{id\}/g, id);
        temp = temp.replace(/\{x\}/g, i);
        temp = temp.replace(/\{width\}/g, '20');
        temp = temp.replace(/\{height\}/g, cellHeight);
        html[idx++] = temp;
        html[idx++] = '&nbsp';
        html[idx++] = '</div>';
        html[idx++] = '</th>';
        html[idx++] = '</tr>';
        html[idx++] = '</thead>';
        html[idx++] = '</table>';
        return html.join('');
    },

    /**
     * Creates the Table Body
     */
    _createTableBody : function(rows) {
        var id = this._mtgId;
        var renderedRowsAllowed = this.renderedRowsAllowed;
        var renderedRows = this.renderedRows;
        var cellHeight = this.cellHeight;
        var headerWidth = this.headerWidth;
        var self = this;
        var html = [];
        var idx = 0;
        var firstRenderingFlg = false;
        if (renderedRows == 0) firstRenderingFlg = true;

        if (firstRenderingFlg) {
            this.innerBodyDiv.setStyle({
                height: (rows.length * cellHeight) + 'px'
                });
            html[idx++] = '<table id="mtgBT'+id+'" border="0" cellspacing="0" cellpadding="0" width="'+headerWidth+'" class="mtgBodyTable">';
            html[idx++] = '<tbody>';
        }
        var lastRowToRender = renderedRows + renderedRowsAllowed;
        if (lastRowToRender > rows.length) lastRowToRender = rows.length;
        this._showLoaderSpinner();
        for (var i = renderedRows; i < lastRowToRender; i++) {
            //            (function(row, rowIdx) {
            //                return function() {
            //                    setTimeout(function(){ html[idx++] = self._createRow(row, rowIdx);},0);
            //                };
            //            })(rows[i],i)();
            html[idx++] = self._createRow(rows[i], i);
            renderedRows++;
        }

        if (firstRenderingFlg) {
            html[idx++] = '</tbody>';
            html[idx++] = '</table>';
        }
        this.renderedRows = renderedRows;
        setTimeout(function(){
            self._hideLoaderSpinner();;
        },1.5); //just to see the spinner
        return html.join('');
    },

    /**
     * Creates a row
     */
    _createRow : function(row, rowIdx) {
        var id = this._mtgId;
        var tdTmpl = '<td id="mtgC{id}_{x},{y}" height="{height}" width="{width}" style="width:{width}px;height:{height}px;padding:0;margin:0;display:{display}" class="mtgCell mtgC{id} mtgC{id}_{x} mtgR{id}_{y}">';
        var icTmpl = '<div id="mtgIC{id}_{x},{y}" style="width:{width}px;height:{height}px;padding:3px;text-align:{align}" class="mtgInnerCell mtgIC{id} mtgIC{id}_{x} mtgIR{id}_{y}">';
        var checkboxTmpl = '<input id="mtgInput{id}_{x},{y}" name="mtgInput{id}_{x},{y}" type="checkbox" value="{value}" class="mtgInput{id}_{x} mtgInputCheckbox">';
        var radioTmpl = '<input id="mtgInput{id}_{x},{y}" name="mtgInput{id}_{x}" type="radio" value="{value}" class="mtgInput{id}_{x} mtgInputRadio">';
        if (Prototype.Browser.Opera || Prototype.Browser.WebKit) {
            checkboxTmpl = '<input id="mtgInput{id}_{x},{y}" name="mtgInput{id}_{x},{y}" type="checkbox" value="{value}" class="mtgInput{id}_{x}">';
            radioTmpl = '<input id="mtgInput{id}_{x},{y}" name="mtgInput{id}_{x}" type="radio" value="{value}" class="mtgInput{id}_{x}">';
        }
        var rs = this.rowStyle || function(){
            return '';
        }; // row style handler
        var rc = this.rowClass || function(){
            return '';
        }; // row class handler
        var cellHeight = this.cellHeight;
        var iCellHeight = cellHeight - 6;
        var cm = this.columnModel;
        var fontSize = this.fontSize;
        var gap = (this.gap == 0)? 2 : 0;
        var html = [];
        var idx = 0;
        html[idx++] = '<tr id="mtgRow'+id+'_'+rowIdx+'" class="mtgRow'+id+' '+rc(rowIdx)+'" style="'+rs(rowIdx)+'">';
        for (var j = 0; j < cm.length; j++) {
            var columnIdx = cm[j].positionIndex;
            var type = cm[j].type || 'string';
            var cellWidth = parseInt(cm[j].width);
            var iCellWidth = cellWidth - 6 - gap;
            var editor = cm[j].editor || null;
            var normalEditorFlg = !(editor == 'checkbox' || editor instanceof MyTableGrid.CellCheckbox || editor == 'radio' || editor instanceof MyTableGrid.CellRadioButton);
            var alignment = 'left';
            var display = '';
            if (!cm[j].hasOwnProperty('renderer')) {
                alignment = (type == 'string')? 'left' : 'right';
            }
            if (cm[j].hasOwnProperty('align')) {
                alignment = cm[j].align;
            }
            if (!cm[j].visible) {
                display = 'none';
            }
            var temp = tdTmpl.replace(/\{id\}/g, id);
            temp = temp.replace(/\{x\}/g, j);
            temp = temp.replace(/\{y\}/g, rowIdx);
            temp = temp.replace(/\{width\}/g, cellWidth);
            temp = temp.replace(/\{height\}/g, cellHeight);
            temp = temp.replace(/\{display\}/g, display);
            html[idx++] = temp;
            temp = icTmpl.replace(/\{id\}/g, id);
            temp = temp.replace(/\{x\}/g, j);
            temp = temp.replace(/\{y\}/g, rowIdx);
            temp = temp.replace(/\{width\}/, iCellWidth);
            temp = temp.replace(/\{height\}/, iCellHeight);
            temp = temp.replace(/\{align\}/, alignment);
            html[idx++] = temp;
            if (normalEditorFlg) { // checkbox is an special case
                if (!cm[j].hasOwnProperty('renderer')) {
                    html[idx++] = this._getStringMask(row[columnIdx], cellWidth, fontSize);
                } else {
                    html[idx++] = cm[j].renderer(row[columnIdx]);
                }
            } else if (editor == 'checkbox' || editor instanceof MyTableGrid.CellCheckbox) {
                temp = checkboxTmpl.replace(/\{id\}/g, id);
                temp = temp.replace(/\{x\}/g, j);
                temp = temp.replace(/\{y\}/g, rowIdx);
                temp = temp.replace(/\{value\}/, row[columnIdx]);
                html[idx++] = temp;
            } else if (editor == 'radio' || editor instanceof MyTableGrid.CellRadioButton) {
                temp = radioTmpl.replace(/\{id\}/g, id);
                temp = temp.replace(/\{x\}/g, j);
                temp = temp.replace(/\{y\}/g, rowIdx);
                temp = temp.replace(/\{value\}/, row[columnIdx]);
                html[idx++] = temp;
            }
            html[idx++] = '</div>';
            html[idx++] = '</td>';
        }
        html[idx++] = '</tr>';
        return html.join('');
    },
    /**
     * Applies cell callbacks
     */
    _applyCellCallbacks : function() {
        for (var i = 0; i < this.columnModel.length; i++) {
            var editor = this.columnModel[i].editor;
            if ((editor instanceof MyTableGrid.CellRadioButton && editor.onClickCallback) ||
                (editor instanceof MyTableGrid.CellCheckbox && editor.onClickCallback)) {
                $$('.mtgInput'+this._mtgId + '_' + i).each(function(element){
                    element.onclick = (function(editor) {
                        return function() {
                            editor.onClickCallback(element.value, element.checked);
                        };
                    })(editor);
                });
            }
        }
    },

    /**
     * Returns an string mask when the string is larger than the containing cell width
     */
    _getStringMask : function(value, width, fontSize) {
        var result = value;
        if (value == null || (value !=null && value.strip().length == 0)) return '';
        if (Math.round(value.length*fontSize*0.6) > width) {
            var nbrChar = Math.round((width / fontSize)*1.5);
            result = value.substring(0, nbrChar - 3) + ' ...';
        }
        return result;
    },

    _showLoaderSpinner : function() {
        var id = this._mtgId;
        var loaderSpinner = $('mtgLoader'+id);
        if(loaderSpinner) loaderSpinner.show();
    },

    _hideLoaderSpinner : function() {
        var id = this._mtgId;
        var loaderSpinner = $('mtgLoader'+id);
        if(loaderSpinner) loaderSpinner.hide();
    },
    /**
     * Creates the Setting Menu
     */
    _createSettingMenu : function() {
        var id = this._mtgId;
        var cm = this.columnModel;
        var bh = this.bodyHeight + 30;
        var cellHeight = (Prototype.Browser.IE)? 25 : 22;
        var height = (cm.length * 25 > bh)? bh : cm.length * cellHeight;
        var html = [];
        var idx = 0;
        html[idx++] = '<div id="mtgSM'+id+'" class="mtgMenu" style="height:'+height+'px">';
        html[idx++] = '<ul>';
        for (var i = 0; i < cm.length; i++) {
            var column = cm[i];
            html[idx++] = '<li>';
            html[idx++] = '<a href="#" class="mtgMenuItem">';
            html[idx++] = '<table border="0" cellpadding="0" cellspacing="0" width="100%">';
            html[idx++] = '<tr><td width="25"><span><input type="checkbox" checked="'+column.visible+'"></span></td>';
            html[idx++] = '<td>&nbsp;'+ column.title+'</td></tr>';
            html[idx++] = '</table>';
            html[idx++] = '</a>';
            html[idx++] = '</li>';
        }
        html[idx++] = '</ul>';
        html[idx++] = '</div>';
        return html.join('');
    },

    /**
     * Applies Setting Menu behavior
     */
    _applySettingMenuBehavior : function() {
        var settingMenu = $('mtgSM' + this._mtgId);
        var width = settingMenu.getWidth();
        var settingButton = $('mtgSB' + this._mtgId);

        Event.observe(settingButton, 'click', function() {
            if (settingMenu.getStyle('visibility') == 'hidden') {
                var topPos = settingButton.offsetTop;
                var leftPos = settingButton.offsetLeft;
                settingMenu.setStyle({
                    top: (topPos + 16) + 'px',
                    left: (leftPos - width + 16) + 'px',
                    visibility: 'visible'
                });
            } else {
                settingMenu.setStyle({
                    visibility: 'hidden'
                });
            }
        });


        if (this.headerTitle) {
            Event.observe(this.headerTitle,'mousemove', function() {
                settingMenu.setStyle({
                    visibility: 'hidden'
                });
            });
        }

        Event.observe(this.bodyTable,'mousemove', function() {
            settingMenu.setStyle({
                visibility: 'hidden'
            });
        });

        var self = this;
        $$('#mtgSM' + this._mtgId + ' input[@type:checkbox]').each(function(checkbox, index) {
            checkbox.onclick = function() {
                self._toggleColumnVisibility(index, checkbox.checked);
            };
        });
    },

    /**
     * Synchronizes horizontal scrolling
     */
    _syncScroll : function() {
        var id = this._mtgId;
        var keys = this.keys;
        var bodyDiv = this.bodyDiv;
        var headerRowDiv = this.headerRowDiv;
        var bodyTable = this.bodyTable;
        var renderedRows = this.renderedRows;

        this.scrollLeft = headerRowDiv.scrollLeft = bodyDiv.scrollLeft;
        this.scrollTop = bodyDiv.scrollTop;

        $('mtgHB' + id).setStyle({
            visibility: 'hidden'
        });
        $('mtgHBM' + id).setStyle({
            visibility: 'hidden'
        });

        if(renderedRows < this.rows.length 
            && (bodyTable.getHeight() - bodyDiv.scrollTop - 10) < bodyDiv.clientHeight) {
            var html = this._createTableBody(this.rows);
            bodyTable.down('tbody').insert(html);
            this._addKeyBehavior();
            keys.addMouseBehavior();
        }
    },

    /**
     * Makes all columns resizable
     */
    _makeAllColumnResizable : function() {
        var id = this._mtgId;
        var headerHeight = this.headerHeight;
        var scrollBarWidth = this.scrollBarWidth;
        var topPos = 0;
        if (this.options.title) topPos += this.headerHeight;
        if (this.options.toolbar) topPos += this.headerHeight;

        var headerButton = $('mtgHB' + this._mtgId);
        var columnIndex;
        var self = this;
        var leftPos = 0;
        $$('.mtgHS' + this._mtgId).each(function(separator, index) {
            Event.observe(separator, 'mousemove', function() {
                headerButton.setStyle({
                    visibility: 'visible'
                });
                columnIndex = -1;
                for (var i = 0; i < self.columnModel.length; i++) {
                    if (index == self.columnModel[i].positionIndex) {
                        columnIndex = i;
                        break;
                    }
                }
                if (columnIndex >= 0) {
                    leftPos = $('mtgHC' + id + '_' + columnIndex).offsetLeft - self.scrollLeft;
                    leftPos += $('mtgHC' + id + '_' + columnIndex).offsetWidth;

                    self.resizeMarkerRight.setStyle({
                        height: (self.bodyHeight + headerHeight) + 'px',
                        top: (topPos + 2) + 'px',
                        left: (leftPos + 1) + 'px'
                    });
                }
            });
        });

        new Draggable(self.resizeMarkerRight, {
            constraint: 'horizontal',
            onStart : function() {
                var markerHeight = self.bodyHeight + headerHeight + 2;
                if (self._hasHScrollBar()) markerHeight = markerHeight - scrollBarWidth + 1;

                self.resizeMarkerRight.setStyle({
                    height: markerHeight + 'px',
                    backgroundColor: 'dimgray'
                });

                var leftPos = $('mtgHC' + id + '_' + columnIndex).offsetLeft - self.scrollLeft;

                self.resizeMarkerLeft.setStyle({
                    height: markerHeight + 'px',
                    top: (topPos + 2) + 'px',
                    left: leftPos + 'px',
                    backgroundColor: 'dimgray'
                });
            },

            onEnd : function() {
                var newWidth = parseInt(self.resizeMarkerRight.getStyle('left')) - parseInt(self.resizeMarkerLeft.getStyle('left'));
                if (newWidth > 0 && columnIndex != null) {
                    setTimeout(function() {
                        self._resizeColumn(columnIndex, newWidth);
                    }, 0);
                }

                self.resizeMarkerLeft.setStyle({
                    backgroundColor: 'transparent',
                    left: 0
                });

                self.resizeMarkerRight.setStyle({
                    backgroundColor: 'transparent'
                });
            },
            endeffect : false
        });
    },

    /**
     * Resizes a column to a new size
     *
     * @param index the index column position
     * @param newWidth resizing width
     */
    _resizeColumn: function(index, newWidth) {
        var id = this._mtgId;
        var cm = this.columnModel;
        var gap = this.gap;
        var self = this;

        var oldWidth = parseInt($('mtgHC' + id + '_' + index).width);
        var editor = cm[index].editor;
        var checkboxOrRadioFlg = (editor == 'checkbox' || editor instanceof MyTableGrid.CellCheckbox || editor == 'radio' || editor instanceof MyTableGrid.CellRadioButton);

        $('mtgHC' + id + '_' + index).width = newWidth;
        $('mtgHC' + id + '_' + index).setStyle({
            width: newWidth + 'px'
            });
        $('mtgIHC' + id + '_' + index).setStyle({
            width: (newWidth - 8 - ((gap == 0) ? 2 : 0)) + 'px'
            });

        $$('.mtgC' + id + '_' + index).each(function(cell) {
            cell.width = newWidth;
            cell.setStyle({
                width: newWidth + 'px'
                });
        });

        $$('.mtgIC' + id + '_' + index).each(function(cell) {
            var cellId = cell.id;
            var coords = cellId.substring(cellId.indexOf('_') + 1, cellId.length).split(',');
            var y = coords[1];
            var value = self.getValueAt(index, y);
            cell.setStyle({
                width: (newWidth - 6 - ((gap == 0) ? 2 : 0)) + 'px'
                });

            if (!checkboxOrRadioFlg) {
                if (cm[index].renderer) value = cm[index].renderer(value);

                if (!value.match(/^<img/)) {
                    cell.innerHTML = self._getStringMask(value, newWidth, self.fontSize);
                } else {
                    cell.innerHTML = value;
                }
            }
        });

        this.headerWidth = this.headerWidth - (oldWidth - newWidth);

        $('mtgHRT' + id).width = this.headerWidth;
        $('mtgBT' + id).width = this.headerWidth;

        this.columnModel[index].width = newWidth;
        this._syncScroll();
    },

    _hasHScrollBar : function() {
        return (this.headerWidth + 20) > this.tableWidth;
    },

    /**
     * Makes all columns draggable
     */
    _makeAllColumnDraggable : function() {
        this.separators = [];
        var i = 0;
        var id = this._mtgId;
        var self = this;
        $$('.mtgHS' + this._mtgId).each(function(separator) {
            self.separators[i++] = separator;
        });

        var topPos = 0;
        if (this.options.title) topPos += this.headerHeight;
        if (this.options.toolbar) topPos += this.headerHeight;

        var dragColumn = $('dragColumn' + id);

        $$('.mtgIHC' + id).each(function(column, index) {
            var columnIndex = -1;
            Event.observe(column, 'mousemove', function() {
                var leftPos = column.up().offsetLeft;
                dragColumn.setStyle({
                    top: (topPos + 15) + 'px',
                    left: (leftPos - self.scrollLeft + 15) + 'px'
                });
            });
            new Draggable(dragColumn, {
                handle : column,
                onStart : function() {
                    for (var i = 0; i < self.columnModel.length; i++) {
                        if (index == self.columnModel[i].positionIndex) {
                            columnIndex = i;
                            break;
                        }
                    }
                    if (Prototype.Browser.IE) {
                        // The drag might register an ondrag or onselectstart event when using IE
                        Event.observe(document.body, "drag", function() {
                            return false;
                        }, false);
                        Event.observe(document.body, "selectstart",	function() {
                            return false;
                        }, false);
                    }
                    dragColumn.down('span').innerHTML = self.columnModel[columnIndex].title;
                    dragColumn.setStyle({
                        visibility: 'visible'
                    });
                },
                onDrag : function() {
                    var leftPos = parseInt(dragColumn.getStyle('left'));
                    var width = parseInt(dragColumn.getStyle('width'));
                    setTimeout(function(){
                        self._detectDroppablePosition(leftPos + width / 2, width, dragColumn, columnIndex);
                    }, 0);
                },
                onEnd : function() {
                    dragColumn.setStyle({
                        visibility: 'hidden'
                    });
                    self.colMoveTopDiv.setStyle({
                        visibility: 'hidden'
                    });
                    self.colMoveBottomDiv.setStyle({
                        visibility: 'hidden'
                    });
                    if (columnIndex >=0 && self.targetColumnId >= 0) {
                        setTimeout(function(){
                            self._moveColumn(columnIndex, self.targetColumnId);
                            columnIndex = -1;
                        }, 0);
                    }
                },
                endeffect : false
            });
        });
    },

    /**
     * Detects dropable position when the mouse pointer is over a header cell
     * separator
     */
    _detectDroppablePosition : function(columnPos, width, dragColumn, index) {
        var topPos = -8;
        if (this.options.title) topPos += this.headerHeight;
        if (this.options.toolbar) topPos += this.headerHeight;
        var sepLeftPos = 0;
        var cm = this.columnModel;
        var gap = this.gap;
        var scrollLeft = this.scrollLeft;
        var colMoveTopDiv = this.colMoveTopDiv;
        var colMoveBottomDiv = this.colMoveBottomDiv;

        for (var i = 0; i < cm.length; i++) {
            if (cm[i].visible) sepLeftPos += parseInt(cm[i].width) + gap;
            if (columnPos > (sepLeftPos - scrollLeft)
                && (columnPos - (sepLeftPos - this.scrollLeft)) < (width / 2)) {
                colMoveTopDiv.setStyle({
                    top: topPos + 'px',
                    left: (sepLeftPos - scrollLeft - 4) + 'px',
                    visibility : 'visible'
                });
                colMoveBottomDiv.setStyle({
                    top: (topPos + 34) + 'px',
                    left: (sepLeftPos - scrollLeft - 4) + 'px',
                    visibility : 'visible'
                });
                this.targetColumnId = i;
                dragColumn.down('div').className = (i != index)? 'drop-yes' : 'drop-no';
                break;
            } else {
                colMoveTopDiv.setStyle({
                    visibility : 'hidden'
                });
                colMoveBottomDiv.setStyle({
                    visibility : 'hidden'
                });
                this.targetColumnId = null;
                dragColumn.down('div').className = 'drop-no';
            }
        }
    },

    /**
     * Moves a column from one position to a new one
     *
     * @param fromColumnId initial position
     * @param toColumnId target position
     */
    _moveColumn : function(fromColumnId, toColumnId) {
        // Some validations
        if (fromColumnId == null
            || toColumnId == null
            || fromColumnId == toColumnId
            || (toColumnId + 1 == fromColumnId && fromColumnId == this.columnModel.length -1)) return;

        var id = this._mtgId;
        var cm = this.columnModel;
        var keys = this.keys;
        var renderedRows = this.renderedRows;
        var numberOfRowsAdded = this.newRowsAdded.length;

        $('mtgHB' + id).setStyle({
            visibility: 'hidden'
        }); // in case the cell menu button is visible
        this._blurCellElement(keys._nCurrentFocus); //in case there is a cell in editing mode
        keys.blur(); //remove the focus of the selected cell

        var removedHeaderCell = null;
        var targetHeaderCell = null;
        var removedCells = null;
        var tr = null;
        var targetId = null;
        var targetCell = null;
        var idx = 0;
        var i = 0;
        var last = null;

        if (toColumnId == 0) { // moving to the left to first column
            removedHeaderCell = $('mtgHC'+id+'_'+fromColumnId).remove();
            targetHeaderCell = $('mtgHC'+id+'_'+ toColumnId);
            targetHeaderCell.up().insertBefore(removedHeaderCell, targetHeaderCell);

            // Moving cell elements
            removedCells = [];
            idx = 0;
            $$('.mtgC'+id+'_'+fromColumnId).each(function(element){
                removedCells[idx++] = element.remove();
            });

            if (numberOfRowsAdded > 0) {
                for (i = -numberOfRowsAdded; i < 0; i++) {
                    targetCell = $('mtgC'+id+'_'+toColumnId+','+i);
                    targetCell.up().insertBefore(removedCells[i+numberOfRowsAdded], targetCell);
                }
            }

            for (i = numberOfRowsAdded; i < (renderedRows+numberOfRowsAdded); i++) {
                targetCell = $('mtgC'+id+'_'+toColumnId+','+(i-numberOfRowsAdded));
                targetCell.up().insertBefore(removedCells[i], targetCell);
            }
        } else if (toColumnId > 0 && toColumnId < cm.length - 1) { // moving in between
            removedHeaderCell = $('mtgHC'+id+'_'+fromColumnId).remove();
            targetId = toColumnId + 1;
            targetHeaderCell = $('mtgHC'+id+'_'+ targetId);
            targetHeaderCell.up().insertBefore(removedHeaderCell, targetHeaderCell);

            // Moving cell elements
            removedCells = [];
            idx = 0;
            $$('.mtgC'+id+'_'+fromColumnId).each(function(element){
                removedCells[idx++] = element.remove();
            });

            if (numberOfRowsAdded > 0) {
                for (i = -numberOfRowsAdded; i < 0; i++) {
                    targetCell = $('mtgC'+id+'_'+targetId+','+i);
                    targetCell.up().insertBefore(removedCells[i+numberOfRowsAdded], targetCell);
                }
            }

            for (i = numberOfRowsAdded; i < (renderedRows+numberOfRowsAdded); i++) {
                targetCell = $('mtgC'+id+'_'+targetId+','+(i-numberOfRowsAdded));
                targetCell.up().insertBefore(removedCells[i], targetCell);
            }
        } else if (toColumnId == cm.length - 1) { // moving to the last column
            tr = $('mtgHC'+id+'_'+fromColumnId).up();
            removedHeaderCell = $('mtgHC'+id+'_'+fromColumnId).remove();
            last = $('mtgHC'+id+'_'+ cm.length);
            tr.insertBefore(removedHeaderCell, last);

            // Moving cell elements
            removedCells = [];
            idx = 0;
            $$('.mtgC'+id+'_'+fromColumnId).each(function(element){
                removedCells[idx++] = element.remove();
            });

            if (numberOfRowsAdded > 0) {
                for (i = -numberOfRowsAdded; i < 0; i++) {
                    tr = $('mtgRow'+id+'_'+i);
                    tr.insert(removedCells[i+numberOfRowsAdded]);
                }
            }

            for (i = numberOfRowsAdded; i < (renderedRows+numberOfRowsAdded); i++) {
                tr = $('mtgRow'+id+'_'+(i-numberOfRowsAdded));
                tr.insert(removedCells[i]);
            }
        }

        // Update column model
        var columnModelLength = cm.length;
        var columnModelEntry = cm[fromColumnId];
        cm[fromColumnId] = null;
        cm = cm.compact();
        var aTemp = [];
        var k = 0;
        var targetColumnId = toColumnId;
        if (toColumnId > 0 && toColumnId < fromColumnId) targetColumnId++;
        for (var c = 0; c < columnModelLength; c++) {
            if (c == targetColumnId) aTemp[k++] = columnModelEntry;
            if (c < (columnModelLength - 1))
                aTemp[k++] = cm[c];
        }
        cm = this.columnModel = aTemp;
        var htr = $('mtgHRT'+id).down('tr');
        htr.getElementsBySelector('th').each(function(th, index){
            if (index < cm.length) {
                th.id = 'mtgHC'+id+'_'+index;
                var ihc = th.down('div');
                ihc.id = 'mtgIHC'+id+'_'+index;
                ihc.down('img').id = 'mtgSortIcon'+id+'_'+index;
                ihc.down('div').id = 'mtgHS'+id+'_'+index;
            }
        });

        // Recreates cell indexes
        for (i = -numberOfRowsAdded; i < renderedRows; i++) {
            $$('.mtgR'+id+'_'+i).each(function(td, index) {
                td.id = 'mtgC'+id+'_'+index+','+i;
                td.className = 'mtgCell mtgC'+id+' mtgC'+id+'_'+index + ' mtgR'+id+'_'+i;
            });

            $$('.mtgIR'+id+'_'+i).each(function(div, index) {
                div.id = 'mtgIC'+id+'_'+index+','+i;
                div.className = 'mtgInnerCell mtgIC'+id+' mtgIC'+id+'_'+index + ' mtgIR'+id+'_'+i;
                if (div.firstChild && div.firstChild.tagName == 'INPUT') { // when it contains a checkbox or radio button
                    var input = div.firstChild;
                    input.id = 'mtgInput'+id+'_'+index+','+i;
                    input.name = 'mtgInput'+id+'_'+index+','+i;
                    input.className =  input.className.replace(/mtgInput.*?_.*?\s/, 'mtgInput'+id+'_'+index+' ');
                }
            });
        }
    },

    /**
     * Add Key behavior functionality to the table grid
     */
    _addKeyBehavior : function() {
        var rows = this.rows;
        var renderedRows = this.renderedRows;
        var renderedRowsAllowed = this.renderedRowsAllowed;
        var beginAtRow = renderedRows - renderedRowsAllowed;
        if (beginAtRow < 0) beginAtRow = 0;
        for (var j = beginAtRow; j < renderedRows; j++) {
            this._addKeyBehaviorToRow(rows[j], j);
        }
    },

    _addKeyBehaviorToRow : function(row, j) {
        var self = this;
        var id = this._mtgId;
        var cm = this.columnModel;
        var keys = this.keys;

        for (var i = 0; i < cm.length; i++) {
            var element = $('mtgC'+id+'_'+i+','+j);
            if (cm[i].editable) {
                keys.event.remove.action(element);
                keys.event.remove.esc(element);
                keys.event.remove.blur(element);

                var f_action = (function(element) {
                    return function() {
                        if (self.editedCellId == null || self.editedCellId != element.id) {
                            self.editedCellId = element.id;
                            self._editCellElement(element);
                        } else {
                            self._blurCellElement(element);
                            self.editedCellId = null;
                        }
                    };
                })(element);
                keys.event.action(element, f_action);

                var f_esc = (function(element) {
                    return function() {
                        self._blurCellElement(element);
                        self.editedCellId = null;
                    };
                })(element);
                keys.event.esc(element, f_esc);

                var f_blur = (function(x, y, element) {
                    return function() {
                        self._blurCellElement(element);
                        self.editedCellId = null;
                        if (self.onCellBlur) self.onCellBlur(element, row[x], x, y, cm[x].id);
                    };
                })(i, j, element);
                keys.event.blur(element, f_blur);
            }

            keys.event.remove.focus(element);
            var f_focus = (function(x, y, element) {
                return function() {
                    if (self.onCellFocus) {
                        self.onCellFocus(element, row[x], x, y, cm[x].id);
                    }
                };
            })(i, j, element);
            keys.event.focus(element, f_focus);
        }
    },

    /**
     *  When a cell is edited
     */
    _editCellElement : function(element) {
        this.keys._bInputFocused = true;
        var width = parseInt(element.getStyle('width'));
        var height = parseInt(element.getStyle('height'));
        var coords = this.getCurrentPosition();
        var x = coords[0];
        var y = coords[1];
        var id = 'mtgIC' + this._mtgId + '_' + x +','+y;
        var innerElement = $(id);
        var value = this.getValueAt(x, y);
        var editor = this.columnModel[x].editor || 'input';
        var type = this.columnModel[x].type || 'string';
        var marginTop = (Prototype.Browser.IE)? '1px' : '2px';
        var input = null;
        var isInputFlg = (editor == 'input' || editor instanceof MyTableGrid.CellInput || editor instanceof MyTableGrid.ComboBox || editor instanceof MyTableGrid.BrowseInput || editor instanceof MyTableGrid.CellCalendar);

        if (isInputFlg) {
            element.setStyle({
                height: this.cellHeight + 'px'
            });

            innerElement.setStyle({
                width: width + 'px',
                height: height + 'px',
                padding: '0',
                border: '0',
                verticalAlign: 'middle',
                textAlign: 'left'
            });

            var alignment = (type == 'number')? 'right' : 'left';

            if (editor == 'input' || editor instanceof MyTableGrid.CellInput) {
                innerElement.innerHTML = '';
                input = new Element('input', {
                    id: 'mtgInput' + this._mtgId + '_' + x + ',' + y,
                    type: 'text',
                    value: value
                });
                input.addClassName('mtgInputText');
                input.setStyle({
                    width: (width - 9) + 'px',
                    height: (height - 10) + 'px',
                    marginTop: marginTop,
                    marginLeft: '1px',
                    paddingLeft: '3px',
                    paddingRight: '3px',
                    textAlign: alignment
                });
                innerElement.appendChild(input);
                input.focus();
                input.select();
            } else {
                value = innerElement.innerHTML;
                innerElement.innerHTML = '';
                input = editor.render(this,{
                    width: width,
                    height: height,
                    value: value,
                    align: alignment
                });
                innerElement.appendChild(input);
                input.down('input').focus();
                input.down('input').select();
            } 
        } else if (editor == 'checkbox' || editor instanceof MyTableGrid.CellCheckbox) {
            input = $('mtgInput' + this._mtgId + '_' + x + ',' + y);
            input.checked = (!input.checked);
            if (editor instanceof MyTableGrid.CellCheckbox && editor.onClickCallback) {
                editor.onClickCallback(value, input.checked);
            }
            this.keys._bInputFocused = false;
            this.editedCellId = null;
        } else if (editor == 'radio' || editor instanceof MyTableGrid.CellRadioButton) {
            input = $('mtgInput' + this._mtgId + '_' + x + ',' + y);
            input.checked = (!input.checked);
            if (editor instanceof MyTableGrid.CellRadioButton && editor.onClickCallback) {
                editor.onClickCallback(value, input.checked);
            }
            this.keys._bInputFocused = false;
            this.editedCellId = null;
        }
    },

    /**
     * When the cell is blured
     */
    _blurCellElement : function(element) {
        if (!this.keys._bInputFocused) return;
        var id = this._mtgId;
        var keys = this.keys;
        var cm = this.columnModel;
        var fs = this.fontSize;
        var width = parseInt(element.getStyle('width'));
        var height = parseInt(element.getStyle('height'));
        var coords = keys.getCoordsFromCell(element);
        var x = coords[0];
        var y = coords[1];
        var cellWidth = cm[x].width;
        var cellHeight = this.cellHeight;

        var innerId = 'mtgIC' + id + '_' + x + ',' + y;
        var input = $('mtgInput' + id + '_' + x + ',' + y);
        var value = input.value;
        var innerElement = $(innerId);
        var editor = cm[x].editor || 'input';
        var type = cm[x].type || 'string';
        var alignment = (type == 'number')? 'right' : 'left';
        var isInputFlg = (editor == 'input' || editor instanceof MyTableGrid.CellInput || editor instanceof MyTableGrid.ComboBox || editor instanceof MyTableGrid.BrowseInput || editor instanceof MyTableGrid.CellCalendar);

        x = this.getColumnIndex(this.columnModel[x].id);

        if (isInputFlg) {
            if (editor.hide) editor.hide(); // this only when the ComboBox editor is used
            if (editor.validate) { // this only when the ComboBox editor is used
                var isValidFlg = editor.validate(value, input);
                if (editor instanceof MyTableGrid.ComboBox && !isValidFlg) {
                    value = editor.getList()[0].text;
                } else {
                    if (!isValidFlg) {
                        if (y >= 0)
                            value = this.rows[y][x];
                        else
                            value = this.newRowsAdded[Math.abs(y)-1][x];
                    }
                }
            } 
            
            element.setStyle({
                height: cellHeight + 'px'
            });

            innerElement.setStyle({
                width : (width - 6) + 'px',
                height : (height - 6) + 'px',
                padding: '3px',
                textAlign: alignment
            }).update(this._getStringMask(value, cellWidth, fs));
        }
        
        if (y >= 0)
            this.rows[y][x] = value;
        else
            this.newRowsAdded[Math.abs(y)-1][x] = value;

        if (editor instanceof MyTableGrid.BrowseInput && editor.afterUpdateCallback) {
            editor.afterUpdateCallback(element, value);
        }

        if (y >= 0 && this.modifiedRows.indexOf(y) == -1) this.modifiedRows.push(y); //if doesn't exist in the array the row is registered
        keys._bInputFocused = false;
    },

    /**
     * Applies header buttons
     */
    _applyHeaderButtons : function() {
        var self = this;
        var id = this._mtgId;
        var headerButton = $('mtgHB' + this._mtgId);
        var headerButtonMenu = $('mtgHBM' + this._mtgId);
        var sortAscMenuItem = $('mtgSortAsc'+this._mtgId);
        var sortDescMenuItem = $('mtgSortDesc'+this._mtgId);
        var topPos = 0;
        if (this.options.title) topPos += this.headerHeight;
        if (this.options.toolbar) topPos += this.headerHeight;
        var selectedHCIndex = -1;
        $$('.mtgIHC' + id).each(function(element, index) {
            var editor = null;
            var sortable = true;
            Event.observe(element, 'mousemove', function() {
                for (var i = 0; i < self.columnModel.length; i++) {
                    if (index == self.columnModel[i].positionIndex) {
                        selectedHCIndex = i;
                        editor = self.columnModel[i].editor;
                        sortable = self.columnModel[i].sortable;
                        break;
                    }
                }

                if (selectedHCIndex == -1) return;
                if (sortable || editor == 'checkbox' || editor instanceof MyTableGrid.CellCheckbox) {
                    headerButtonMenu.setStyle({
                        visibility: 'hidden'
                    });
                    var hc = element.up();
                    var leftPos = hc.offsetLeft + hc.offsetWidth;
                    leftPos = leftPos - 16 - self.scrollLeft;
                    if (leftPos < self.bodyDiv.clientWidth) {
                        headerButton.setStyle({
                            top: (topPos + 3) + 'px',
                            left: leftPos + 'px',
                            visibility: 'visible'
                        });
                    }
                    sortAscMenuItem.onclick = function() {
                        $('mtgSortIcon'+id+'_'+selectedHCIndex).src = self._imageRefs.mtgSortArrowAscIcon;
                        self.request[self.sortColumnParameter] = self.columnModel[selectedHCIndex].id;
                        self.request[self.ascDescFlagParameter] = 'ASC';
                        self._retrieveDataFromUrl(1);
                        $('mtgSortIcon'+id+'_'+self.sortedColumnIndex).hide();
                        $('mtgSortIcon'+id+'_'+selectedHCIndex).show();
                        self.sortedColumnIndex = selectedHCIndex;
                    };

                    sortDescMenuItem.onclick = function() {
                        $('mtgSortIcon'+id+'_'+selectedHCIndex).src = self._imageRefs.mtgSortArrowDescIcon;
                        self.request[self.sortColumnParameter] = self.columnModel[selectedHCIndex].id;
                        self.request[self.ascDescFlagParameter] = 'DESC';
                        self._retrieveDataFromUrl(1);
                        $('mtgSortIcon'+id+'_'+self.sortedColumnIndex).hide();
                        $('mtgSortIcon'+id+'_'+selectedHCIndex).show();
                        self.sortedColumnIndex = selectedHCIndex;
                    };
                }
            });
        });

        Event.observe(headerButton, 'click', function() {
            if (headerButtonMenu.getStyle('visibility') == 'hidden') {
                if (self.columnModel[selectedHCIndex].sortable) {
                    $('mtgSortDesc'+self._mtgId).show();
                    $('mtgSortAsc'+self._mtgId).show();
                } else {
                    $('mtgSortDesc'+self._mtgId).hide();
                    $('mtgSortAsc'+self._mtgId).hide();
                }

                var selectAllItem = $$('#mtgHBM' + id + ' .mtgSelectAll')[0];
                if (self.columnModel[selectedHCIndex].editor == 'checkbox' ||
                    self.columnModel[selectedHCIndex].editor instanceof MyTableGrid.CellCheckbox) {
                    selectAllItem.show();
                    // var index = self.columnModel[selectedHCIndex].positionIndex;
                    selectAllItem.onclick = function() {
                        var flag = $('mtgSelectAll' + id).checked;
                        $$('.mtgInput' + id + '_' + selectedHCIndex).each(function(element) {
                            element.checked = flag;
                        });

                        if (self.columnModel[selectedHCIndex].editor instanceof MyTableGrid.CellCheckbox
                            && self.columnModel[selectedHCIndex].editor.onClickCallback) {
                            self.columnModel[selectedHCIndex].editor.onClickCallback();
                        }
                    };
                } else {
                    selectAllItem.hide();
                }

                var leftPos = parseInt(headerButton.getStyle('left'));
                var topPos = self.headerHeight + 2;
                if (self.options.title) topPos += self.headerHeight;
                if (self.options.toolbar) topPos += self.headerHeight;
                headerButtonMenu.setStyle({
                    top: topPos + 'px',
                    left: leftPos + 'px',
                    visibility: 'visible'
                });
            } else {
                headerButtonMenu.setStyle({
                    visibility: 'hidden'
                });
            }
        });


        Event.observe(this.bodyTable,'mousemove', function() {
            headerButtonMenu.setStyle({
                visibility: 'hidden'
            });
        });
    },

    _toggleColumnVisibility : function(index, visibleFlg) {
        this._blurCellElement(this.keys._nCurrentFocus); //in case there is a cell in editing mode
        this.keys.blur(); //remove the focus of the selected cell
        var headerRowTable = $('mtgHRT' + this._mtgId);
        var bodyTable = $('mtgBT' + this._mtgId);

        for (var i = 0; i < this.columnModel.length; i++) {
            if (this.columnModel[i].positionIndex == index) {
                index = i;
                break;
            }
        }

        var targetColumn = $('mtgHC' + this._mtgId + '_' + index);
        $('mtgHB' + this._mtgId).setStyle({
            visibility: 'hidden'
        });

        var width = 0;

        if (!visibleFlg) { // hide
            width = parseInt(targetColumn.offsetWidth);
            targetColumn.hide();
            $$('.mtgC'+this._mtgId+ '_'+index).each(function(element){
                element.hide();
            });
            this.columnModel[index].visible = false;
            this.headerWidth = this.headerWidth - width;
        } else { // show
            targetColumn.show();
            width = parseInt(targetColumn.offsetWidth);
            $$('.mtgC'+this._mtgId+ '_'+index).each(function(element){
                element.show();
            });
            this.columnModel[index].visible = true;
            this.headerWidth = this.headerWidth + width;
        }

        headerRowTable.width = this.headerWidth;
        headerRowTable.setStyle({
            width: this.headerWidth + 'px'
            });
        bodyTable.width = this.headerWidth;
        bodyTable.setStyle({
            width: this.headerWidth + 'px'
            });
    },

    _fullPadding : function(element, s) {
        var padding = parseInt(element.getStyle('padding-'+s));
        padding = (isNaN(padding)) ? 0 : padding;
        var border = parseInt(element.getStyle('border-'+s+'-width'));
        border = (isNaN(border)) ? 0 : border;
        return padding + border;
    },

    _retrieveDataFromUrl : function(pageNumber, firstTimeFlg) {
        var pageParameter = 'page';
        if(this.pager != null && this.pager.pageParameter) pageParameter = this.pager.pageParameter;
        this.request[pageParameter] = pageNumber;
        this._showLoaderSpinner();
        var self = this;
        new Ajax.Request(this.url, {
            parameters: self.request,
            onSuccess: function(transport) {
                var tableModel = transport.responseText.evalJSON();
                self.rows = tableModel.rows || [];
                self.pager = null;
                if (tableModel.options != null && tableModel.options.pager) self.pager = tableModel.options.pager;
                if (tableModel.rows.length > 0) {
                    self.renderedRows = 0;
                    self.innerBodyDiv.innerHTML = self._createTableBody(tableModel.rows);
                    self.bodyTable = $('mtgBT' + self._mtgId);
                    if (!firstTimeFlg) {
                        self._applyCellCallbacks();
                        self.keys = new KeyTable(self);
                        self._addKeyBehavior();
                    }
                }
                if (self.pager) {
                    self.pagerDiv.innerHTML = self._updatePagerInfo(); // update pager info panel
                    self._addPagerBehavior();
                }

                if (self.afterRender) {
                    self.afterRender();
                }
                self._hideLoaderSpinner();
                if (firstTimeFlg) self.bodyDiv.fire('dom:dataLoaded');
            }
        });
    },

    _updatePagerInfo : function(emptyFlg) {
        var id = this._mtgId;
        var imageRefs = this._imageRefs;

        if (emptyFlg)
            return '<span id="mtgLoader'+id+'" style="display:none"><img src="'+imageRefs.mtgLoaderIcon+'" width="16" height="16"></span>';

        var html = [];
        var idx = 0;
        var pager = this.pager;

        if (this.pager.total > 0) {
            var temp = this._messages.totalDisplayMsg;
            temp = temp.replace(/\{total\}/g, pager.total);
            if (pager.from && pager.to) {
                temp += this._messages.rowsDisplayMsg;
                temp = temp.replace(/\{from\}/g, pager.from);
                temp = temp.replace(/\{to\}/g, pager.to);
            }
            html[idx++] = '<span class="mtgPagerMsg">'+temp+'</span>';
            if (pager.pages) {
                temp = this._messages.pagePromptMsg;
                temp = temp.replace(/\{pages\}/g, pager.pages);
                var input = '<input type="text" name="mtgPageInput'+id+'" id="mtgPageInput'+id+'" value="'+pager.currentPage+'" class="mtgPageInput" size="3" maxlength="3">';
                temp = temp.replace(/\{input\}/g, input);
                html[idx++] = '<table class="mtgPagerTable" border="0" cellpadding="0" cellspacing="0">';
                html[idx++] = '<tbody>';
                html[idx++] = '<tr>';

                html[idx++] = '<td><span id="mtgLoader'+id+'" style="display:none"><img src="'+imageRefs.mtgLoaderIcon+'" width="16" height="16"></span></td>';
                html[idx++] = '<td><img src="'+imageRefs.ySepIcon+'" class="mtgSep" width="2" height="13"></td>';
                html[idx++] = '<td><a id="mtgFirst'+id+'" class="mtgPagerCtrl"><img src="'+imageRefs.firstIcon+'" class"mtgFirstPage" width="16" height="16" border="0"></a></td>';
                html[idx++] = '<td><a id="mtgPrev'+id+'" class="mtgPagerCtrl"><img src="'+imageRefs.prevIcon+'" class="mtgPrevPage" width="16" height="16" border="0"></a></td>';
                html[idx++] = '<td><img src="'+imageRefs.ySepIcon+'" class="mtgSep" width="2" height="13"></td>';
                html[idx++] = temp;

                html[idx++] = '<td><img src="'+imageRefs.ySepIcon+'" class="mtgSep" width="2" height="13"></td>';
                html[idx++] = '<td><a id="mtgNext'+id+'" class="mtgPagerCtrl"><img src="'+imageRefs.nextIcon+'" class="mtgNextPage" width="16" height="16" border="0"></a></td>';
                html[idx++] = '<td><a id="mtgLast'+id+'" class="mtgPagerCtrl"><img src="'+imageRefs.lastIcon+'" class="mtgLastPage" width="16" height="16" border="0"></a></td>';
                html[idx++] = '</tr>';
                html[idx++] = '</table>';
            } else {
                html[idx++] = '<table class="mtgPagerTable" border="0" cellpadding="0" cellspacing="0">';
                html[idx++] = '<tbody>';
                html[idx++] = '<tr>';
                html[idx++] = '<td><span id="mtgLoader'+id+'" style="display:none"><img src="'+imageRefs.mtgLoaderIcon+'" width="16" height="16"></span></td>';
                html[idx++] = '</tr>';
                html[idx++] = '</table>';
            }
        } else {
            html[idx++] = '<span class="mtgPagerMsg">'+this._messages.pagerNoDataFound+'</span>';
        }
        return html.join('');
    },

    _addPagerBehavior : function() {
        var self = this;
        if (!self.pager.pages) return;
        var currentPage = self.pager.currentPage;
        var pages = self.pager.pages;
        var total = self.pager.total;
        if (total > 0) {
            if (currentPage > 1) {
                $('mtgFirst'+this._mtgId).down('img').src = self._imageRefs.firstIcon;
                $('mtgFirst'+this._mtgId).onclick = function() {
                    self._retrieveDataFromUrl.call(self, 1);
                };
            } else {
                $('mtgFirst'+this._mtgId).down('img').src = self._imageRefs.firstDisabledIcon;
            }

            if (currentPage > 0 && currentPage < pages) {
                $('mtgNext'+this._mtgId).down('img').src = self._imageRefs.nextIcon;
                $('mtgNext'+this._mtgId).onclick = function() {
                    self._retrieveDataFromUrl.call(self, currentPage + 1);
                };
            } else {
                $('mtgNext'+this._mtgId).down('img').src = self._imageRefs.nextDisabledIcon;
            }

            if (currentPage > 1 && currentPage <= pages) {
                $('mtgPrev'+this._mtgId).down('img').src = self._imageRefs.prevIcon;
                $('mtgPrev'+this._mtgId).onclick = function() {
                    self._retrieveDataFromUrl.call(self, currentPage - 1);
                };
            } else {
                $('mtgPrev'+this._mtgId).down('img').src = self._imageRefs.prevDisabledIcon;
            }

            if (currentPage < pages) {
                $('mtgLast'+this._mtgId).down('img').src = self._imageRefs.lastIcon;
                $('mtgLast'+this._mtgId).onclick = function() {
                    self._retrieveDataFromUrl.call(self, self.pager.pages);
                };
            } else {
                $('mtgLast'+this._mtgId).down('img').src = self._imageRefs.lastDisabledIcon;
            }

            var keyHandler = function(event) {
                if (event.keyCode == Event.KEY_RETURN) {
                    var pageNumber = $('mtgPageInput'+self._mtgId).value;
                    if (pageNumber > pages) pageNumber = pages;
                    if (pageNumber < 1) pageNumber = '1';
                    $('mtgPageInput'+self._mtgId).value = pageNumber;
                    self._retrieveDataFromUrl.call(self, pageNumber);
                }
            };

            if (Prototype.Browser.Gecko || Prototype.Browser.Opera ) {
                Event.observe($('mtgPageInput'+this._mtgId), 'keypress', function(event) {
                    keyHandler(event);
                });
            } else {
                Event.observe($('mtgPageInput'+this._mtgId), 'keydown', function(event) {
                    keyHandler(event);
                });
            }
        }
    },

    resize : function() {
        var target = $(this.target);
        var width = this.options.width || (target.getWidth() - this._fullPadding(target,'left') - this._fullPadding(target,'right')) + 'px';
        var height = this.options.height || (target.getHeight() - this._fullPadding(target,'top') - this._fullPadding(target,'bottom')) + 'px';
        this.tableWidth = parseInt(width) - 2;
        var tallerFlg = false;
        if ((parseInt(height) - 2) > this.tableHeight) tallerFlg = true;
        this.tableHeight = parseInt(height) - 2;

        this.tableDiv.setStyle({
            width: this.tableWidth + 'px',
            height: this.tableHeight + 'px'
        });

        if (this.headerTitle) {
            this.headerTitle.setStyle({
                width: (this.tableWidth - 6) + 'px'
            });
        }

        if (this.headerToolbar) {
            this.headerToolbar.setStyle({
                width: (this.tableWidth - 6) + 'px'
            });
        }

        this.headerRowDiv.setStyle({
            width: (this.tableWidth) + 'px'
        });

        this.bodyHeight = this.tableHeight - this.headerHeight - 3;
        if (this.options.title) this.bodyHeight = this.bodyHeight - this.headerHeight - 1;
        if (this.options.pager) this.bodyHeight = this.bodyHeight - this.pagerHeight;
        if (this.options.toolbar) this.bodyHeight = this.bodyHeight - this.pagerHeight;

        this.bodyDiv.setStyle({
            width: (this.tableWidth) + 'px',
            height: this.bodyHeight + 'px'
        });

        if (this.options.pager) {
            var topPos = this.bodyHeight + this.headerHeight + 3;
            if (this.options.title) topPos += this.headerHeight;
            if (this.options.toolbar) topPos += this.headerHeight;
            this.pagerDiv.setStyle({
                top: topPos + 'px',
                width: (this.tableWidth - 2) + 'px'
            });
        }

        this.renderedRowsAllowed = Math.floor(this.bodyDiv.clientHeight / this.cellHeight);
        
        if (tallerFlg) {
            var html = this._createTableBody(this.rows);
            this.bodyTable.down('tbody').insert(html);
            this._addKeyBehavior();
            this.keys.addMouseBehavior();
        }
    },

    getValueAt : function(x, y) {
        var value = null;
        x = this.getColumnIndex(this.columnModel[x].id);
        if (y >= 0)
            value = this.rows[y][x];
        else
            value = this.newRowsAdded[Math.abs(y)-1][x];
        return value;
    },

    setValueAt : function(value, x, y, refreshValueFlg) {
        if(refreshValueFlg == undefined || refreshValueFlg) $('mtgIC'+this._mtgId+'_'+x+','+y).innerHTML = value;
        x = this.getColumnIndex(this.columnModel[x].id);
        if (y >= 0)
            this.rows[y][x] = value;
        else
            this.newRowsAdded[Math.abs(y)-1][x] = value;
    },

    getColumnIndex : function(id) {
        var index = -1;
        for (var i = 0; i < this.columnModel.length; i++) {
            if (this.columnModel[i].id == id) {
                index = this.columnModel[i].positionIndex;
                break;
            }
        }
        return index;
    },

    getIndexOf : function(id) {
        var i = 0;
        for (; i < this.columnModel.length; i++) {
            if (this.columnModel[i].id == id) break;
        }
        return i;
    },

    getCurrentPosition : function() {
        return [this.keys._xCurrentPos, this.keys._yCurrentPos];
    },

    getCellElementAt : function(x, y) {
        return $('mtgC'+this._mtgId + '_' + x + ',' + y);
    },

    getModifiedRows : function() {
        var result = [];
        var modifiedRows = this.modifiedRows;
        var rows = this.rows;
        var cm = this.columnModel;

        for (var i = 0; i < modifiedRows.length; i++) {
            var idx = modifiedRows[i];
            var row = {};
            for (var j = 0; j < cm.length; j++) {
                row[cm[j].id] = rows[idx][cm[j].positionIndex];
            }
            result.push(row);
        }
        return result;
    },

    getNewRowsAdded : function() {
        var result = [];
        var newRowsAdded = this.newRowsAdded;
        var cm = this.columnModel;
        
        for (var i = 0; i < newRowsAdded.length; i++) {
            if (newRowsAdded[i] != null) {
                var row = {};
                for (var j = 0; j < cm.length; j++) {
                    row[cm[j].id] = newRowsAdded[i][cm[j].positionIndex];
                }
                result.push(row);
            }
        }
        return result;
    },

    getDeletedRows : function() {
        return this.deletedRows;    
    },

    _getSelectedRowsIdx: function() {
        var result = [];
        var id = this._mtgId;
        var cm = this.columnModel;
        var newRowsAdded = this.newRowsAdded;
        var renderedRows = this.renderedRows;
        var idx = -1; // Selectable column index
        for (var i = 0; i < cm.length; i++) {
            if (cm[i].editor instanceof MyTableGrid.CellCheckbox
                && cm[i].editor.selectable) {
                idx = cm[i].positionIndex;
                break;
            }
        }
        if (idx >=0) {
            var j = 0;
            var y = 0;
            if (newRowsAdded.length > 0) { // there are new rows added
                for (j = 0; j < newRowsAdded.length; j++) {
                    y = -(j + 1);
                    if($('mtgInput'+id+'_'+idx+','+y).checked) result.push(y);
                }
            }

            for (j = 0; j < renderedRows; j++) {
                y = j;
                if($('mtgInput'+id+'_'+idx+','+y).checked) result.push(y);
            }
        }
        return result;
    },

    highlightRow : function(id, value) {
        $$('.mtgRow'+this._mtgId).each(function(row){
            row.removeClassName('focus');
        });

        var index = this.getColumnIndex(id);
        var rowIndex = -1;
        for (var i = 0; i < this.rows.length; i++) {
            if (this.rows[i][index] == value) {
                rowIndex = i;
                break;
            }
        }

        if (rowIndex >= 0) {
            $('mtgRow'+this._mtgId+'_'+rowIndex).addClassName('focus');
        }
    },

    getRow : function(y) {
        var cm = this.columnModel;
        var result = {};
        for (var x = 0; x < cm.length; x++) {
            var value = null;
            if (y >= 0)
                value = this.rows[y][cm[x].positionIndex];
            else
                value = this.newRowsAdded[-(y+1)][cm[x].positionIndex];
            result[cm[x].id] = value;
        }
        return result;
    },

    clear : function() {
        this.modifiedRows = [];
    },
    
    addNewRow : function() {
        var keys = this.keys;
        var bodyTable = this.bodyTable;
        var cm = this.columnModel;
        var i = this.newRowsAdded.length + 1;
        var newRow = [];
        for (var j = 0; j < cm.length; j++) {
            newRow[j] = '';
        }
        bodyTable.down('tbody').insert({
            top: this._createRow(newRow, -i)
            });
        this.newRowsAdded[i-1] = newRow;
        keys.setTopLimit(-i);
        this._addKeyBehaviorToRow(newRow, -i);
        keys.addMouseBehaviorToRow(-i);
        this.scrollTop = this.bodyDiv.scrollTop = 0;
    },

    deleteRows : function() {
        var id = this._mtgId;
        var selectedRows = this._getSelectedRowsIdx();
        alert(selectedRows);
        var i = 0;
        var y = 0;
        for (i = 0; i < selectedRows.length; i++) {
            y = selectedRows[i];
            if (y >=0) {
                this.deletedRows.push(this.getRow(y));
            } else {
                this.newRowsAdded[Math.abs(y)-1] = null;
            }
            $('mtgRow'+id+'_'+y).hide();
        }
    },

    refresh : function() {
        this._retrieveDataFromUrl(1,false);
    }
};